using Sage.CloudStorage.Qiniu.Auth;
using Sage.CloudStorage.Qiniu.Config;
using Sage.CloudStorage.Qiniu.Models;
using Sage.Http.Core;
using Sage.Http.Models;
using System.Net.Http.Headers;

namespace Sage.CloudStorage.Qiniu.Storage
{
    /// <summary>
    /// 上传管理器 - 提供文件上传相关功能
    /// </summary>
    /// <remarks>
    /// 创建上传管理器实例
    /// </remarks>
    /// <param name="httpManager">HTTP请求管理器</param>
    /// <param name="key">七牛云密钥信息</param>
    /// <param name="config">七牛云配置</param>
    public class UploadManager(HttpRequestManager httpManager, QiniuKey key, QiniuConfig config)
    {
        private readonly HttpRequestManager _httpManager = httpManager ?? throw new ArgumentNullException(nameof(httpManager));
        private readonly QiniuKey _key = key ?? throw new ArgumentNullException(nameof(key));
        private readonly QiniuConfig _config = config ?? throw new ArgumentNullException(nameof(config));

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="filePath">本地文件路径</param>
        /// <param name="key">目标文件键名</param>
        /// <param name="bucket">存储桶名称</param>
        /// <param name="policy">上传策略（可选）</param>
        /// <param name="progress">上传进度回调（可选）</param>
        /// <param name="cancellationToken">取消令牌（可选）</param>
        /// <returns>上传结果</returns>
        public async Task<UploadResult> UploadFileAsync(
            string filePath,
            string bucket,
            string? key = null,
            UploadPolicy? policy = null,
            IProgress<float>? progress = null,
            CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrEmpty(filePath))
                throw new ArgumentNullException(nameof(filePath));

            if (!File.Exists(filePath))
                throw new FileNotFoundException("上传的文件不存在", filePath);

            if (string.IsNullOrEmpty(bucket))
                throw new ArgumentNullException(nameof(bucket));

            // 使用文件流上传
            using var fileStream = File.OpenRead(filePath);
            return await UploadStreamAsync(fileStream, bucket, key, policy, progress, cancellationToken);
        }

        /// <summary>
        /// 上传数据流
        /// </summary>
        /// <param name="stream">数据流</param>
        /// <param name="key">目标文件键名</param>
        /// <param name="bucket">存储桶名称</param>
        /// <param name="policy">上传策略（可选）</param>
        /// <param name="progress">上传进度回调（可选）</param>
        /// <param name="cancellationToken">取消令牌（可选）</param>
        /// <returns>上传结果</returns>
        public async Task<UploadResult> UploadStreamAsync(
            Stream stream,
            string bucket,
            string? key = null,
            UploadPolicy? policy = null,
            IProgress<float>? progress = null,
            CancellationToken cancellationToken = default)
        {
            ArgumentNullException.ThrowIfNull(stream);
            ArgumentNullException.ThrowIfNull(bucket);

            if (string.IsNullOrEmpty(bucket))
                throw new ArgumentNullException(nameof(bucket));

            // 创建或使用提供的上传策略
            policy ??= new UploadPolicy
            {
                Scope = string.IsNullOrEmpty(key) ? bucket : $"{bucket}:{key}",
                Deadline = (uint)DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds()
            };

            // 创建上传凭证
            string uploadPolicy = policy.ToJson();
            var authProvider = new QiniuAuthProvider(_key, uploadPolicy);

            // 准备表单数据
            var formData = new Dictionary<string, string>();
            if (!string.IsNullOrEmpty(key))
            {
                formData.Add("key", key);
            }

            // 上传文件
            string url = _config.GetUpHost();
            var request = _httpManager.CreateRequest(HttpMethod.Post, url)
                .WithAuthentication(authProvider)
                .AddFormFields(formData)
                .AddFile(new FileParameter(stream, "file", "file", "application/octet-stream"));

            if (progress != null)
                request = request.WithProgress(progress);

            // 发送请求并解析响应
            var result = await request.SendAsJsonAsync(QiniuJsonSerializerContext.Default.UploadResult, cancellationToken);

            // 确保返回非空结果
            return result ?? new UploadResult { Key = key ?? string.Empty };
        }

        /// <summary>
        /// 上传字节数组
        /// </summary>
        /// <param name="data">字节数组</param>
        /// <param name="key">目标文件键名</param>
        /// <param name="bucket">存储桶名称</param>
        /// <param name="policy">上传策略（可选）</param>
        /// <param name="progress">上传进度回调（可选）</param>
        /// <param name="cancellationToken">取消令牌（可选）</param>
        /// <returns>上传结果</returns>
        public async Task<UploadResult> UploadDataAsync(
            byte[] data,
            string key,
            string bucket,
            UploadPolicy? policy = null,
            IProgress<float>? progress = null,
            CancellationToken cancellationToken = default)
        {
            if (data == null || data.Length == 0)
                throw new ArgumentNullException(nameof(data));

            using var stream = new MemoryStream(data);
            return await UploadStreamAsync(stream, bucket, key, policy, progress, cancellationToken);
        }

        /// <summary>
        /// 创建分片上传会话
        /// </summary>
        /// <param name="key">目标文件键名</param>
        /// <param name="bucket">存储桶名称</param>
        /// <param name="policy">上传策略（可选）</param>
        /// <returns>分片上传会话</returns>
        public Task<ChunkedUploadSession> CreateChunkedUploadSessionAsync(
            string key,
            string bucket,
            UploadPolicy? policy = null)
        {
            if (string.IsNullOrEmpty(bucket))
                throw new ArgumentNullException(nameof(bucket));

            // 创建或使用提供的上传策略
            policy ??= new UploadPolicy
            {
                Scope = string.IsNullOrEmpty(key) ? bucket : $"{bucket}:{key}",
                Deadline = (uint)DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds()
            };

            // 创建分片上传会话
            return Task.FromResult(new ChunkedUploadSession(_httpManager, _key, _config, key, policy));
        }
    }

    /// <summary>
    /// 分片上传会话
    /// </summary>
    public class ChunkedUploadSession
    {
        private readonly HttpRequestManager _httpManager;
        private readonly QiniuKey _key;
        private readonly QiniuConfig _config;
        private readonly string _key_name;
        private readonly UploadPolicy _policy;
        private readonly List<string> _blockContexts = [];
        private readonly int _blockSize;

        /// <summary>
        /// 创建分片上传会话
        /// </summary>
        internal ChunkedUploadSession(
            HttpRequestManager httpManager,
            QiniuKey key,
            QiniuConfig config,
            string keyName,
            UploadPolicy policy)
        {
            _httpManager = httpManager;
            _key = key;
            _config = config;
            _key_name = keyName;
            _policy = policy;
            _blockSize = config.BlockSize;
        }

        /// <summary>
        /// 上传分块
        /// </summary>
        /// <param name="blockIndex">分块索引</param>
        /// <param name="blockData">分块数据</param>
        /// <param name="progress">上传进度回调（可选）</param>
        /// <param name="cancellationToken">取消令牌（可选）</param>
        /// <returns>操作是否成功</returns>
        public async Task<bool> UploadBlockAsync(
            int blockIndex,
            byte[] blockData,
            IProgress<float>? progress = null,
            CancellationToken cancellationToken = default)
        {
            if (blockData == null || blockData.Length == 0)
                throw new ArgumentNullException(nameof(blockData));

            if (blockData.Length > _blockSize)
                throw new ArgumentException($"分块大小不能超过{_blockSize}字节", nameof(blockData));

            // 创建上传凭证
            string uploadPolicy = _policy.ToJson();
            var authProvider = new QiniuAuthProvider(_key, uploadPolicy);

            // 上传分块
            string url = $"{_config.GetUpHost()}/mkblk/{blockData.Length}";
            using var content = new ByteArrayContent(blockData);
            content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

            var request = _httpManager.CreateRequest(HttpMethod.Post, url)
                .WithAuthentication(authProvider)
                .WithContent(content);

            if (progress != null)
                request = request.WithProgress(progress);

            try
            {
                // 发送请求并解析响应
                var result = await request.SendAsJsonAsync(QiniuJsonSerializerContext.Default.BlockUploadResult, cancellationToken);
                if (result == null)
                    return false;

                // 保存块上下文
                while (_blockContexts.Count <= blockIndex)
                {
                    _blockContexts.Add(string.Empty);
                }
                _blockContexts[blockIndex] = result.Ctx;

                return true;
            }
            catch
            {
                return false;
            }


        }

        /// <summary>
        /// 完成分片上传
        /// </summary>
        /// <param name="fileSize">文件总大小</param>
        /// <param name="cancellationToken">取消令牌（可选）</param>
        /// <returns>上传结果</returns>
        public async Task<UploadResult> CompleteUploadAsync(
            long fileSize,
            CancellationToken cancellationToken = default)
        {
            if (_blockContexts.Count == 0 || _blockContexts.Any(string.IsNullOrEmpty))
                throw new InvalidOperationException("尚未完成所有分块的上传");

            // 创建上传凭证
            string uploadPolicy = _policy.ToJson();
            var authProvider = new QiniuAuthProvider(_key, uploadPolicy);

            // 合并分块
            string url = $"{_config.GetUpHost()}/mkfile/{fileSize}";
            if (!string.IsNullOrEmpty(_key_name))
            {
                url += $"/key/{Base64UrlSafeEncode(_key_name)}";
            }

            // 构建分块上下文
            string ctxs = string.Join(",", _blockContexts);
            using var content = new StringContent(ctxs);
            content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");

            // 发送请求并解析响应
            var result = await _httpManager.CreateRequest(HttpMethod.Post, url)
                .WithAuthentication(authProvider)
                .WithContent(content)
                .SendAsJsonAsync(QiniuJsonSerializerContext.Default.UploadResult, cancellationToken);

            // 确保返回非空结果
            return result ?? new UploadResult { Key = _key_name };
        }

        /// <summary>
        /// Base64 URL安全编码
        /// </summary>
        /// <param name="text">要编码的文本</param>
        /// <returns>编码后的文本</returns>
        private static string Base64UrlSafeEncode(string text)
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(text);
            string base64 = Convert.ToBase64String(bytes);
            return base64.Replace("+", "-").Replace("/", "_");
        }
    }

    #region 结果类

    /// <summary>
    /// 上传结果
    /// </summary>
    public class UploadResult
    {
        /// <summary>
        /// 文件哈希值（ETAG）
        /// </summary>
        public string Hash { get; set; } = string.Empty;

        /// <summary>
        /// 文件键名
        /// </summary>
        public string Key { get; set; } = string.Empty;
    }

    /// <summary>
    /// 分块上传结果
    /// </summary>
    internal class BlockUploadResult
    {
        /// <summary>
        /// 块上下文
        /// </summary>
        public string Ctx { get; set; } = string.Empty;

        /// <summary>
        /// 校验和
        /// </summary>
        public string Checksum { get; set; } = string.Empty;

        /// <summary>
        /// 块偏移
        /// </summary>
        public long Offset { get; set; }

        /// <summary>
        /// 主机
        /// </summary>
        public string Host { get; set; } = string.Empty;
    }

    #endregion
}